﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using SKYPE4COMLib;
using System.Windows.Forms;

namespace BackLib
{
    public class SkypeWrapper
    {
        private SKYPE4COMLib.SkypeClass skype = new SkypeClass();
 
        #region Enums

        public enum OnlineStatusFriendly
        {
            Online,
            Busy,
            Away,
            Blocked,
            Invisible
        }

        public enum CallStatusFriendly
        {
            Ringing,
            InProgress,
            OnHold,
            CallEnded
        }

        public enum UserType
        {
            Standard,
            S4PG
        }

        #endregion

        #region Events

        public event CurrentUserMoodUpdatedHandler CurrentUserMoodUpdated;
        public delegate void CurrentUserMoodUpdatedHandler(string mood);

        public event CurrentUserStatusUpdatedHandler CurrentUserStatusUpdated;
        public delegate void CurrentUserStatusUpdatedHandler(OnlineStatusFriendly status);

        public event ChatUpdatedHandler ChatUpdated;
        public delegate void ChatUpdatedHandler(Chat chat);

        public event UserUpdatedHandler UserUpdated;
        public delegate void UserUpdatedHandler(User user);

        public event CallUpdatedHandler CallUpdated;
        public delegate void CallUpdatedHandler(Call call);

        public event UsersListedHandler UsersListed;
        public delegate void UsersListedHandler(UserCollection users);

        public event ChatsListedHandler ChatsListed;
        public delegate void ChatsListedHandler(ChatCollection users);

        public event CallsListedHandler CallsListed;
        public delegate void CallsListedHandler(CallCollection calls);

        public event MessageReceivedHandler MessageReceived;
        public delegate void MessageReceivedHandler(ChatMessage message);

        public event MuteChangedHandler MuteChanged;
        public delegate void MuteChangedHandler(bool muted);

        #endregion

        #region Startup Events

        private bool initialized = false;
        private void AttachmentStatus(TAttachmentStatus status)
        {
            switch (status)
            {
                case TAttachmentStatus.apiAttachPendingAuthorization:
                    break;
                case TAttachmentStatus.apiAttachSuccess:
                    if (!initialized)
                    {
                        initialized = true;
                        InitializeCurrentUser();
                    }
                    break;
                case TAttachmentStatus.apiAttachRefused:
                    break;
            }
        }

        #endregion

        #region Startup Methods

        public void ConnectToSkype()
        {
            CreateSkypeEventHandlers();
            if (!skype.Client.IsRunning)
            {
                skype.Client.Start(true, true);
                for (int i = 1; i <= 50; i++)
                {
                    System.Threading.Thread.Sleep(400);
                    if (skype.Client.IsRunning) break;
                }
            }

            if (!skype.Client.IsRunning)
            {
                MessageBox.Show("Could not start Skype. Application cannot function.");
                return;
            }

            for (int i = 1; i <= 50; i++)
            {
                try
                {
                    this.skype.Attach(9, true);
                    break;
                }
                catch { System.Threading.Thread.Sleep(300); }
            }
        }

        private void CreateSkypeEventHandlers()
        {
            skype._ISkypeEvents_Event_AttachmentStatus += new _ISkypeEvents_AttachmentStatusEventHandler(AttachmentStatus);
            skype._ISkypeEvents_Event_CallStatus += new _ISkypeEvents_CallStatusEventHandler(skype__ISkypeEvents_Event_CallStatus);
            skype.MessageStatus += new _ISkypeEvents_MessageStatusEventHandler(skype_MessageStatus);
            skype.OnlineStatus += new _ISkypeEvents_OnlineStatusEventHandler(skype_OnlineStatus);
            skype.UserMood += new _ISkypeEvents_UserMoodEventHandler(skype_UserMood);
            skype.ChatMembersChanged += new _ISkypeEvents_ChatMembersChangedEventHandler(skype_ChatMembersChanged);
            skype._ISkypeEvents_Event_Mute += new _ISkypeEvents_MuteEventHandler(skype__ISkypeEvents_Event_Mute);
        }

        void skype__ISkypeEvents_Event_Mute(bool Mute)
        {
            MuteChanged(Mute);
        }

        private void InitializeCurrentUser()
        {
            User currentUser = skype.CurrentUser;
            CurrentUserMoodUpdated(currentUser.MoodText);
            CurrentUserStatusUpdated(GetOnlineStatusFriendly(currentUser));
            LoadContacts();
        }

        private void LoadContacts()
        {
            //skype.SilentMode = true;
            UsersListed(skype.Friends);
            ChatsListed(skype.ActiveChats);
            CallsListed(skype.ActiveCalls);
        }

        #endregion

        #region Event Handlers

        private void skype_MessageStatus(ChatMessage pMessage, TChatMessageStatus Status)
        {
            switch (Status)
            {
                case TChatMessageStatus.cmsReceived:
                    MessageReceived(pMessage);
                    break;
                case TChatMessageStatus.cmsSending:
                    MessageReceived(pMessage);
                    break;
            }
        }

        private void skype_ChatMembersChanged(Chat pChat, UserCollection pMembers)
        {
            ChatUpdated(pChat);
        }

        private void skype__ISkypeEvents_Event_CallStatus(Call pCall, TCallStatus Status)
        {
            switch (Status)
            {
                case TCallStatus.clsCancelled:
                    CallUpdated(pCall);
                    break;
                case TCallStatus.clsRefused:
                    CallUpdated(pCall);
                    break;
                case TCallStatus.clsBusy:
                    CallUpdated(pCall);
                    break;
                case TCallStatus.clsFinished:
                    CallUpdated(pCall);
                    break;
                case TCallStatus.clsInProgress:
                    CheckForPlannedJoins(pCall);
                    CallUpdated(pCall);
                    break;
                case TCallStatus.clsRinging:
                    CallUpdated(pCall);
                    break;
                case TCallStatus.clsOnHold:
                    CallUpdated(pCall);
                    break;
                case TCallStatus.clsLocalHold:
                    CallUpdated(pCall);
                    break;
                case TCallStatus.clsRemoteHold:
                    CallUpdated(pCall);
                    break;
                case TCallStatus.clsFailed:
                    CallUpdated(pCall);
                    break;
                case TCallStatus.clsMissed:
                    CallUpdated(pCall);
                    break;
                default:
                    break;
            }
        }

        private void skype_UserMood(User pUser, string MoodText)
        {
            if (pUser.Handle == skype.CurrentUser.Handle) CurrentUserMoodUpdated(MoodText);
            else
            {
                UserUpdated(pUser);
            }
        }

        private void skype_OnlineStatus(User pUser, TOnlineStatus Status)
        {
            if (pUser.Handle == skype.CurrentUser.Handle)
            {
                CurrentUserStatusUpdated(GetOnlineStatusFriendly(pUser));
            }
            else
            {
                UserUpdated(pUser);
            }
        }

        #endregion

        #region Public Methods

        public Chat CreateNewChat(string[] handles)
        {
            UserCollection UC = new UserCollection();
            foreach(string handle in handles)
            {
                UC.Add(UserFromHandle(handle));
            }
            return skype.CreateChatMultiple(UC);
        }

        public bool IsServer(string handle)
        {
            User user = skype.get_User(handle);
            return (GetDisplayName(user).Contains("! S4PG.us | ") && user.Homepage == "http://S4PG.us");
        }

        public bool UserIsBlocked(string Handle)
        {
            return skype.get_User(Handle).IsBlocked;
        }

        public void PlaceCall(string Handle)
        {
            skype.PlaceCall(Handle, null, null, null);
        }

        public void MuteToggle(bool muted)
        {
            skype.Mute = muted;
        }

        public bool MuteStatus
        {
            get
            {
                return skype.Mute;
            }
        }

        public void UpdateMood(string Mood)
        {
            skype.CurrentUserProfile.MoodText = Mood;
        }

        public void UpdateStatus(SkypeWrapper.OnlineStatusFriendly Status)
        {
            skype.ChangeUserStatus(GetOnlineStatusSkype(Status));
        }

        public void OpenConversation(int indexValue)
        {
            
        }

        public void LeaveConversation(int indexValue)
        {

        }

        public void CallConversation(int indexValue)
        {

        }

        public void ToggleBlockedStatus(string Handle)
        {
            //User user = UserFromHandle(Handle); ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ;
            //bool isBlocked = user.IsBlocked; 
            //user.IsBlocked = !isBlocked; 
        }

        public void RemoveContact(int indexValue)
        {

        }

        public void AddPeopleToCall(bool isGroup, int ID, string[] handles)
        {

            Call callToJoin;
            if (isGroup)
            {
                //callToJoin = skype.get_Conference(ID).ActiveCalls[0];
                callToJoin = skype.get_Call(ID);
            }
            else
            {
                callToJoin = skype.get_Call(ID);
            }

            Call callToHold = null;
            if (isGroup) skype.get_Conference(ID).Hold();
            foreach (Call call in skype.ActiveCalls)
            {
                foreach (string userhandle in handles)
                {
                    if (call.PartnerHandle == userhandle)
                    {
                        foreach (Call callx in skype.ActiveCalls)
                        {
                            if (callx.PartnerHandle == userhandle)
                            {
                                call.Join(callx.Id);
                                return;
                            }
                        }
                        
                    }
                }
                if (call.Status == TCallStatus.clsInProgress)
                {
                    callToHold = call;
                    call.Hold();
                }
            }
            if (callToHold != null)
            {
                for (int i = 0; i < 100; i++)
                {
                    if (callToHold.Status == TCallStatus.clsInProgress)
                        System.Threading.Thread.Sleep(50);
                    else break;
                }
            }

            int newCallID = skype.PlaceCall(string.Join(",", handles), null, null, null).Id;

            PlannedCallJoinIDs.Add(new object[] { newCallID, callToJoin });
        }

        public void EndCall(int ID, bool groupCall)
        {
            if (groupCall)
            {
                foreach (Call call in skype.get_Conference(ID).ActiveCalls)
                {
                    call.Finish();
                }
            }
            else
            {
                skype.get_Call(ID).Finish();
            }
        }

        List<object[]> PlannedCallJoinIDs = new List<object[]>();

        public void CheckForPlannedJoins(Call call)
        {
            for (int i = 0; i < PlannedCallJoinIDs.Count; i++)
            {
                object[] objects = PlannedCallJoinIDs[i];
                if ((int)objects[0] == call.Id)
                {
                    ((Call)objects[1]).Join(call.Id);
                    PlannedCallJoinIDs.RemoveAt(i);
                }
            }
        }

        public void ToggleCallHold(int ID, bool groupCall)
        {
            CallStatusFriendly status = GetCallStatusFriendly(ID, groupCall);
            if (status == CallStatusFriendly.OnHold)
            {
                if (groupCall) skype.get_Conference(ID).Resume();
                else skype.get_Call(ID).Resume();
            }
            else if (status == CallStatusFriendly.InProgress)
            {
                if (groupCall) skype.get_Conference(ID).Hold();
                else skype.get_Call(ID).Hold();
            }
        }

        public bool CanResumeCall(int callId, bool isGroup)
        {
            return (isGroup ? skype.get_Conference(callId).ActiveCalls[0] : skype.get_Call(callId)).Status == TCallStatus.clsLocalHold;
        }

        public CallStatusFriendly GetCallStatusFriendly(int ID, bool isGroup)
        {
            if (isGroup)
            {
                bool onHold = true;
                Conference conference = skype.get_Conference(ID);
                foreach (Call call in conference.ActiveCalls)
                {
                    if (GetCallStatusFriendly(call) != CallStatusFriendly.OnHold) return CallStatusFriendly.InProgress;
                }
                return CallStatusFriendly.OnHold;
            }
            else
            {
                return GetCallStatusFriendly(skype.get_Call(ID));
            }
        }

        public CallStatusFriendly GetCallStatusFriendly(Call call)
        {
            switch (call.Status)
            {
                case TCallStatus.clsRinging:
                    return CallStatusFriendly.Ringing;
                case TCallStatus.clsInProgress:
                    return CallStatusFriendly.InProgress;
                case TCallStatus.clsLocalHold:
                    return CallStatusFriendly.OnHold;
                case TCallStatus.clsOnHold:
                    return CallStatusFriendly.OnHold;
                case TCallStatus.clsRemoteHold:
                    return CallStatusFriendly.OnHold;
                case TCallStatus.clsCancelled:
                    return CallStatusFriendly.CallEnded;
                case TCallStatus.clsFailed:
                    return CallStatusFriendly.CallEnded;
                case TCallStatus.clsFinished:
                    return CallStatusFriendly.CallEnded;
                case TCallStatus.clsMissed:
                    return CallStatusFriendly.CallEnded;
                case TCallStatus.clsRefused:
                    return CallStatusFriendly.CallEnded;
                case TCallStatus.clsUnplaced:
                    return CallStatusFriendly.CallEnded;
                case TCallStatus.clsBusy:
                    return CallStatusFriendly.CallEnded;
            }

            return CallStatusFriendly.CallEnded;
        }

        public OnlineStatusFriendly GetOnlineStatusFriendly(User user)
        {
            if (user.IsBlocked) return OnlineStatusFriendly.Blocked;
            switch (user.OnlineStatus)
            {
                case TOnlineStatus.olsOnline:
                    return OnlineStatusFriendly.Online;
                case TOnlineStatus.olsDoNotDisturb:
                    return OnlineStatusFriendly.Busy;
                case TOnlineStatus.olsAway:
                    return OnlineStatusFriendly.Away;
                case TOnlineStatus.olsOffline:
                    return OnlineStatusFriendly.Invisible;
                default:
                    return OnlineStatusFriendly.Invisible;
            }
        }

        public TUserStatus GetOnlineStatusSkype(OnlineStatusFriendly onlineStatus)
        {
            switch (onlineStatus)
            {
                case OnlineStatusFriendly.Online:
                    return TUserStatus.cusOnline;
                case OnlineStatusFriendly.Busy:
                    return TUserStatus.cusDoNotDisturb;
                case OnlineStatusFriendly.Away:
                    return TUserStatus.cusAway;
                case OnlineStatusFriendly.Invisible:
                    return TUserStatus.cusInvisible;
                default:
                    return TUserStatus.cusOffline;
            }
        }

        public string GetDisplayName(User user)
        {
            string nameToReturn = user.Handle;
            if (user.FullName != "") nameToReturn = user.FullName;
            if (user.DisplayName != "") nameToReturn = user.DisplayName;
            return nameToReturn;
        }

        public string GetChatName(Chat chat)
        {
            string nameToReturn = chat.DialogPartner;
            if (chat.FriendlyName != "") nameToReturn = chat.FriendlyName;
            if (chat.Topic != "") nameToReturn = chat.Topic;
            return nameToReturn;
        }

        public bool IsGroupCall(Call call)
        {
            return call.ConferenceId != 0;
        }

        public bool ConferenceDead(Call call)
        {
            return skype.get_ConferenceActiveCalls(call.ConferenceId.ToString()).Count == 0;
        }

        public List<User> GetCallMembers(Call call)
        {
            bool groupCall = IsGroupCall(call);

            if (groupCall)
            {
                foreach (Conference conference in skype.Conferences)
                {
                    if (call.ConferenceId == conference.Id)
                    {
                        List<User> users = new List<User>();
                        foreach (Call callx in conference.ActiveCalls)
                        {
                            users.Add(UserFromHandle(callx.PartnerHandle));
                        }
                        return users;
                    }
                }
                return null;
            }
            else
            {
                return new List<User> { UserFromHandle(call.PartnerHandle) };
            }
        }

        public string GetCallName(List<User> users)
        {
            List<string> nameToReturn = new List<string>();
            foreach (User user in users)
            {
                nameToReturn.Add(GetDisplayName(user));
            }
            return String.Join(", ", nameToReturn.ToArray());
        }

        public User UserFromHandle(string handle)
        {
            return skype.get_User(handle);
        }

        #endregion
    }
}
